const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const copyWebpackPlugin = require('copy-webpack-plugin');
const rimraf = require('rimraf');
const CheckDoneHooksPlugin = require('./config/webpack-plugins/wecode-check-done-plugin');

const {
  appPath,
  commonPath,
  toCommonPath,
  pluginPath,
  buildPath,
  appHtml,
  appEntry,
  eslintNodeModules,
  publicPath,
  baseURL,
  webpackPublicPath
} = require('./config/paths');
const { getWeCodeConfig } = require('./config/utils')
const { log } = require('../log');

function clearCache(nodeModulesPathList) {
  nodeModulesPathList.forEach(dir => {
    rimraf(path.join(dir, '.cache'), error => {
      if (error) {
        log(error);
        return;
      }
    });
  });
  log(`Clear cache.`);
}

/**
 * 支持开发自定义postcss配置
 */
function getPostCssConfig() {
  const postcssConfigFilePath = path.join(appPath, `postcss.config.js`);
  const exist = fs.existsSync(postcssConfigFilePath);
  if (exist) {
    try {
      const config = require(postcssConfigFilePath);
      return { ...config }
    } catch (error) {
      warn('getPostCssConfig#Error', error);
    }
  }
  return {
    plugins: [require('autoprefixer')]
  }
}

/**
 * 获取默认babel配置
 */
const defaultBabelConfig = require(path.join(__dirname, 'babel.config.js'));

/**
 * 允许用户指定义babel，通过babel.config.js，但需通过 require.resolve 引用对应的插件，如：require.resolve('babel-plugin-import')
 */
function getUserBabelConfig() {
  const filePath = path.join(appPath, 'babel.config.js');
  if (fs.existsSync(filePath)) {
    try {
      delete require.cache[require.resolve(filePath)];
      const content = require(filePath);
      return content;
    } catch (error) {
      error('Error in webpack.config.js:', error);
    }
  }
  return {};
}

/**
 * 支持开发使用vue.config.js文件，但仅有configureWebpack生效，避免影响模板自定义的配置
 */
function getUserWebpackConfig() {
  const filePath = path.join(appPath, 'vue.config.js');
  if (fs.existsSync(filePath)) {
    try {
      delete require.cache[require.resolve(filePath)];
      const content = require(filePath);
      return content.configureWebpack || {};
    } catch (error) {
      error('Error in webpack.config.js:', error);
    }
  }
  return {};
}

/**
   * 自定义引入node_modules包后再自定义babelc规则会引起找不到包路径,
   * @param {*} loaderPath
   * @returns
   */

function requireResolve(moduleName) {
  return require.resolve(path.join([path.resolve(appPath, './node_modules')][0], moduleName))
}
function getUserBabelPath(loaderPath) {
  let babelLoader = path.join([path.resolve(appPath, './node_modules')][0], loaderPath);
  if (fs.existsSync([path.resolve(appPath, './node_modules')][0]) && fs.existsSync(babelLoader)) {
    return require.resolve(babelLoader);
  }
  return requireResolve(loaderPath);
}
// 整合传入数组 getRulesArr
function getRulesArr() {
  return [
    // 如果需要 eslint, 则使用指定 eslint 配置
    eslintNodeModules && {
      enforce: 'pre',
      test: /\.(vue|(j|t)sx?)$/,
      exclude: /node_modules/,
      use: [
        {
          loader: require.resolve(path.join(eslintNodeModules, '/eslint-loader')),
          options: {
            eslintPath: path.resolve(path.join(eslintNodeModules, '/eslint'))
          }
        }
      ]
    },
    {
      test: /(\.js)$/,
      exclude: /node_modules/,
      use: [
        {
          loader: requireResolve('babel-loader'),
          options: defaultBabelConfig
        },
        Object.keys(getUserBabelConfig()).length > 0 && {
          loader: getUserBabelPath('babel-loader'),
          options: getUserBabelConfig()
        }
      ].filter(Boolean)
    }
  ]
}
// config里面的plugins和module的处理
function getConfig(config) {
  config.plugins.delete('html')
  config.plugins.delete('preload');
  config.plugins.delete('prefetch');
  config.plugins.delete('prefetch-index');
  // 移除所有内置cache-loader
  config.module.rule('vue').uses.delete('cache-loader');
  config.module.rule('js').uses.delete('cache-loader');
  config.module.rule('ts').uses.delete('cache-loader');
  config.module.rule('tsx').uses.delete('cache-loader');
  config.module.rule('js').uses.delete('babel-loader');
  // 移除内置 eslint-loader, 使用自定义 eslint
  // 否则会出现 eslint 抛错但是依然打包完成的问题
  config.module.rules.delete('eslint');
  config.plugins.delete('hmr');
  config.plugins.delete('copy');
  return config
}


// 设置devServer
function getDevServer(devServer) {
  return {
    port:8080,
    client: {
      webSocketURL: `ws://0.0.0.0:${devServer.port}/ws`,
      overlay: {
        errors: true,
        warnings: false
      },
      progress: false // 不输出进度信息
    },
    headers: {
      'Access-Control-Allow-Origin': '*',
    },
    ...devServer,
    historyApiFallback: true,
    allowedHosts: "all",
    hot: true,
    open: false
  }
}

module.exports = function () {
  // 获取app相关信息
  const {
    WECODE_ENV = 'development',
    LOCAL_TEST = 'false',
    WEOCDE_PROCESS_MESSAGE
  } = process.env;
  const ISLOCALDEV = WECODE_ENV === 'development';
  log(`webpack version: ${webpack.version}`);
  log(`ESLint:${eslintNodeModules ? 'true' : 'false'}`);
  if (appPath === undefined) {
    error('AppPath is null!');
    process.exit(0);
  }
  // 获取 wecode.json 文件配置
  const { wecodeJson } = getWeCodeConfig(appPath);
  const { devServer } = wecodeJson;
  function resolveTemplate(dir = '', templateRoot = appPath) {
    return path.join(templateRoot, dir);
  }
  // 指定从哪些node_modules 中查找依赖包,优先从用户目录, 即 template/node_modules 目录中查找，
  // 其次是 sdk 中的 node_modules.asar，调试模板时(LOCAL_TEST==='true')前两个目录可能没有, 则在 runtime/node_moduels 中查找
  const nodeModulesSearchPathList = [
    path.resolve(appPath, './node_modules')
  ];
  // 清除 cache
  clearCache(nodeModulesSearchPathList);
  const postcssConfig = getPostCssConfig();
  let webpackConfigs = {
    context: appPath,
    plugins: [
      new CleanWebpackPlugin(),
      new copyWebpackPlugin({
        patterns:[
          { from: publicPath, to: buildPath },
          ISLOCALDEV && { from: commonPath, to: toCommonPath },
          { from: pluginPath, to: path.join(buildPath, 'plugin.json') }
        ].filter(Boolean)
      } 
      ),
      new CheckDoneHooksPlugin({
        message: WEOCDE_PROCESS_MESSAGE
      }),
      new webpack.DefinePlugin({
        'process.env.APP_ENV': JSON.stringify(WECODE_ENV),
        'process.env.NODE_ENV': JSON.stringify(WECODE_ENV)
      }),
    ],
    resolve: {
      alias: {
        '@': resolveTemplate('src')
      },
      modules: nodeModulesSearchPathList
    },
    module: {
      rules: getRulesArr().filter(Boolean)
    }
  };
  const userWebpackConfig = getUserWebpackConfig();
  webpackConfigs = merge(webpackConfigs, userWebpackConfig);
  return {
    parallel: false,
    // 生产环境才允许 lint
    lintOnSave: !!eslintNodeModules && !ISLOCALDEV,
    // 打包后的代码路径, 对应webpack中的output.path
    outputDir: buildPath,
    // 对应webpack中的output.publicPath
    publicPath: webpackPublicPath,
    pages: {
      index: {
        // 入口js
        entry: appEntry,
        // 模板路径
        template: appHtml,
        filename: baseURL,
        // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
        title: '',
        // 在这个页面中包含的块，默认情况下会包含提取出来的通用 chunk 和 vendor chunk。
        chunks: ['chunk-vendors', 'chunk-common', 'index'],
        vconsole: process.env.WECODE_DEBUG
      }
    },
    devServer: getDevServer(devServer),
    css: {
      loaderOptions: {
        postcss: {
          postcssOptions : postcssConfig
        }
      },
      extract: false
    },
    chainWebpack: config => {
      getConfig(config)
      // 开发阶段不压缩代码
      if (process.env.WECODE_DEBUG == 'true') {
        config.optimization.minimize(false);
      }
    },
    configureWebpack: webpackConfigs
  };
};
